#ifndef VM_H
#define VM_H
#include "types.h"
#include "kalloc.h"

// 单核、恒等映射假设（若将来改为高半内核，只需修改 PA2VA/VA2PA）
#define PGSIZE 4096
#define PGMASK (PGSIZE - 1)
#define PGROUNDUP(sz) (((sz) + PGSIZE - 1) & ~PGMASK)
#define PGROUNDDOWN(a) ((a) & ~PGMASK)

// PTE 操作宏
#define PTE_PA(pte) (((pte) >> 10) << 12)    // 从 PTE 提取物理地址（页对齐）
#define PA2PTE(pa) ((((uint64_t)(pa)) >> 12) << 10) // 物理地址转换为 PPN 字段
#define PTE_FLAGS(pte) ((pte) & 0x3FF)   // 低 10 位为 flags

// RISC-V Sv39 PTE 位（常用）
#define PTE_V (1L << 0)
#define PTE_R (1L << 1)
#define PTE_W (1L << 2)
#define PTE_X (1L << 3)
#define PTE_U (1L << 4)
#define PTE_G (1L << 5)
#define PTE_A (1L << 6)
#define PTE_D (1L << 7)

// 恒等映射：物理地址 <-> 内核可直接访问的虚拟地址（当前实现假设恒等）
#define PA2VA(pa) ((void*)(pa))
#define VA2PA(va) ((uint64_t)(va))

// 虚拟地址索引提取
#define VPN_SHIFT(level) (12 + 9 * (level))
#define VPN_MASK(va, level) (((va) >> VPN_SHIFT(level)) & 0x1FF)

// Sv39 最大虚拟地址
#define MAXVA (1L << 39)

// 可根据 kalloc.c 保持一致
#define KERNBASE 0x80000000UL
#define PHYSTOP   0x88000000UL  // 128MB physical region example
#define UART0     0x10000000UL

// SATP 构造（MODE=8 -> Sv39）
// MAKE_SATP 需要传入页表的物理基地址（PA >> 12）
#define MAKE_SATP(pt_pa) (((uint64_t)8ULL << 60) | ((((uint64_t)(pt_pa)) >> 12) & ((1ULL << 44) - 1)))

typedef uint64_t pte_t;
typedef pte_t* pagetable_t;

// 页表接口
pagetable_t create_pagetable(void);
int map_page(pagetable_t pt, uint64_t va, uint64_t pa, int perm);
int map_region(pagetable_t pt, uint64_t va, uint64_t pa, uint64_t size, int perm);
void destroy_pagetable(pagetable_t pt);
pte_t* walk_create(pagetable_t pt, uint64_t va);
pte_t* walk_lookup(pagetable_t pt, uint64_t va);
void dump_pagetable(pagetable_t pt, int level);

// kernel pagetable root (定义在 vm.c)
extern pagetable_t kernel_pagetable;

// kvminit/kvminithart
void kvminit(void);
void kvminithart(void);

#endif
